﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/9 18:54:59
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ImageK.Java;
using ImageK.Measure;
using ImageK.Plugin.Frame;
using ImageK.Plugin.Tool;
using ImageK.Util;
using Microsoft.VisualBasic.ApplicationServices;

namespace ImageK.Gui
{
    /// <summary>
    /// This is a Canvas used to display images in a Window.
    /// </summary>
    public class ImageCanvas: PictureBox
    {
        protected static Cursor defaultCursor = Cursors.Default;
        protected static Cursor handCursor = Cursors.Hand;
        protected static Cursor moveCursor = Cursors.Arrow;
        protected static Cursor crosshairCursor = Cursors.Cross;

        public static bool usePointer = Prefs.usePointerCursor;

        protected ImagePlus imp;
        protected bool imageUpdated;

        protected Rectangle srcRect;
        protected int imageWidth, imageHeight;
        protected int xMouse; // current cursor offscreen x location 
        protected int yMouse; // current cursor offscreen y location

        private bool showCursorStatus = true;
        private int sx2, sy2;
        private bool disablePopupMenu;

        private static Color zoomIndicatorColor;
        private static Font smallFont, largeFont;
        private Font font;
        private Rectangle[] labelRects;
        private bool maxBoundsReset;
        private Overlay showAllOverlay;
        private const int LIST_OFFSET = 100000;
        private static Color showAllColor = Prefs.getColor(Prefs.SHOW_ALL_COLOR, Color.FromArgb(0, 255, 255));
        private Color defaultColor = showAllColor;
        private static Color labelColor, bgColor;
        private int resetMaxBoundsCount;
        private Roi currentRoi;
        private int mousePressedX, mousePressedY;
        private long mousePressedTime;
        private bool _overOverlayLabel;

        /** If the mouse moves less than this in screen pixels, successive zoom operations are on the same image pixel */
        protected const int MAX_MOUSEMOVE_ZOOM = 10;
        /** Screen coordinates where the last zoom operation was done (initialized to impossible value) */
        protected int lastZoomSX = -9999999;
        protected int lastZoomSY = -9999999;
        /** Image (=offscreen) coordinates where the cursor was moved to for zooming */
        protected int zoomTargetOX = -1;
        protected int zoomTargetOY;

        protected ImageK ij;
        protected double magnification;

        protected int dstWidth, dstHeight;

        protected int xMouseStart;
        protected int yMouseStart;
        protected int xSrcStart;
        protected int ySrcStart;
        protected KeysMouseFlag flags;

        private Bitmap offScreenImage;
        private int offScreenWidth = 0;
        private int offScreenHeight = 0;
        private bool mouseExited = true;
        private bool customRoi;
        private bool drawNames;
        private volatile bool paintPending;
        private bool scaleToFit;
        private bool painted;
        private bool hideZoomIndicator;
        private bool flattening;
        // private Timer pressTimer;
        // private PopupMenu roiPopupMenu;
        private static int longClickDelay = 1000; //ms

        public ImageCanvas(ImagePlus imp)
        {
            this.imp = imp;
            paintPending = false;
            ij = IJ.GetInstance();
            int width = imp.getWidth();
            int height = imp.getHeight();
            imageWidth = width;
            imageHeight = height;
            srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
            setSize(imageWidth, imageHeight);
            magnification = 1.0;

            this.MouseEnter += mouseEntered;
            this.MouseDown += mousePressed;
            this.MouseUp += mouseReleased;
            this.MouseMove += mouseMoved;

            // addMouseListener(this);
            // addMouseMotionListener(this);
            // addKeyListener(ij);  // ImageJ handles keyboard shortcuts
            // setFocusTraversalKeysEnabled(false);
        }

        public void updateImage(ImagePlus imp)
        {
            this.imp = imp;
            int width = imp.getWidth();
            int height = imp.getHeight();
            imageWidth = width;
            imageHeight = height;
            srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
            setSize(imageWidth, imageHeight);
            magnification = 1.0;
        }

        /** Update this ImageCanvas to have the same zoom and scale settings as the one specified. */
        public void update(ImageCanvas ic)
        {
            if (ic==null || ic==this || ic.imp==null)
                return;
            if (ic.imp.getWidth()!=imageWidth || ic.imp.getHeight()!=imageHeight)
                return;
            srcRect = new Rectangle(ic.srcRect.X, ic.srcRect.Y, ic.srcRect.Width, ic.srcRect.Height);
            setMagnification(ic.magnification);
            setSize(ic.dstWidth, ic.dstHeight);
        }

        /** Sets the region of the image (in pixels) to be displayed. */
        public void setSourceRect(Rectangle r)
        {
            if (r==null)
                return;
            r = new Rectangle(r.X, r.Y, r.Width, r.Height);
            imageWidth = imp.getWidth();
            imageHeight = imp.getHeight();
            if (r.X<0) r.X = 0;
            if (r.Y<0) r.Y = 0;
            if (r.Width<1)
                r.Width = 1;
            if (r.Height<1)
                r.Height = 1;
            if (r.Width>imageWidth)
                r.Width = imageWidth;
            if (r.Height>imageHeight)
                r.Height = imageHeight;
            if (r.X+r.Width>imageWidth)
                r.X = imageWidth-r.Width;
            if (r.Y+r.Height>imageHeight)
                r.Y = imageHeight-r.Height;
            if (srcRect==null)
                srcRect = r;
            else
            {
                srcRect.X = r.X;
                srcRect.Y = r.Y;
                srcRect.Width = r.Width;
                srcRect.Height = r.Height;
            }
            if (dstWidth==0)
            {
                Size size = this.Size;
                dstWidth = size.Width;
                dstHeight = size.Height;
            }
            magnification = (double)dstWidth/srcRect.Width;
            imp.setTitle(imp.getTitle());
            if (IJ.debugMode) IJ.log("setSourceRect: "+magnification+" "+(int)(srcRect.Height*magnification+0.5)+" "+dstHeight+" "+srcRect);
        }

        void setSrcRect(Rectangle srcRect)
        {
            setSourceRect(srcRect);
        }

        public Rectangle getSrcRect()
        {
            return srcRect;
        }

        /** Obsolete; replaced by setSize() */
        public void setDrawingSize(int width, int height)
        {
            dstWidth = width;
            dstHeight = height;
            setSize(dstWidth, dstHeight);
        }

        public void setSize(int width, int height)
        {
            //todo:
            //super.setSize(width, height);
            this.Width = width;
            this.Height = height;
            dstWidth = width;
            dstHeight = height;
        }

        /** ImagePlus.updateAndDraw calls this method to force the paint()
            method to update the image from the ImageProcessor. */
        public void setImageUpdated()
        {
            imageUpdated = true;
        }

        public void setPaintPending(bool state)
        {
            paintPending = state;
        }

        public bool getPaintPending()
        {
            return paintPending;
        }

        public void update(Graphics g)
        {
            Draw();
        }

        private static int _saveCount;
        /// <summary>
        /// 替代原来的OnPaint，在控件重绘前，更新相应的参数和图像
        /// </summary>
        public void Draw()
        {
            painted = true;
            Roi roi = imp.getRoi();
            Overlay overlay = imp.getOverlay();
            if (roi != null || overlay != null || showAllOverlay != null || Prefs.paintDoubleBuffered || (IJ.isLinux() && magnification < 0.25))
            {
                // Use double buffering to avoid flickering of ROIs and to work around
                // a Linux problem with large images not showing at low magnification.
                if (roi != null)
                    roi.updatePaste();
                if (imageWidth != 0)
                {
                    paintDoubleBuffered(this.CreateGraphics());
                    // setPaintPending(false);
                    return;
                }

                
            }

            if (imageUpdated)
            {
                imageUpdated = false;
                imp.updateImage();
            }

            // setInterpolation(g, Prefs.interpolateScaledImages);
            this.Image = this.imp.getImage();
            this.Refresh();

            //todo: Roi Overlay
            // this.Width = (int)(srcRect.Width * magnification + 0.5);
            // this.Height = (int)(srcRect.Height * magnification + 0.5);
            // base.OnPaint(pe);
        }

        private void setInterpolation(Graphics g, bool interpolate)
        {
            //todo:
            // if (magnification == 1)
            //     return;
            // else if (magnification < 1.0 || interpolate)
            // {
            //     Object value = RenderingHints.VALUE_RENDER_QUALITY;
            //     ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_RENDERING, value);
            // }
            // else if (magnification > 1.0)
            // {
            //     Object value = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
            //     ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_INTERPOLATION, value);
            // }
        }

        private void drawRoi(Roi roi, Graphics g)
        {
            if (roi == currentRoi)
            {
                Color lineColor = roi.getStrokeColor();
                Color fillColor = roi.getFillColor();
                float lineWidth = roi.getStrokeWidth();
                roi.setStrokeColor(Color.Empty);
                roi.setFillColor(Color.Empty);
                bool strokeSet = roi.getStroke() != null;
                if (strokeSet)
                    roi.setStrokeWidth(1);
                roi.draw(g);
                roi.setStrokeColor(lineColor);
                if (strokeSet)
                    roi.setStrokeWidth(lineWidth);
                roi.setFillColor(fillColor);
                currentRoi = null;
            }
            else
                roi.draw(g);
        }

        public int getSliceNumber(String label)
        {
            if (label==null) return 0;
            int slice = 0;
            if (label.Length>=14 && label[4]=='-' && label[9]=='-')
                slice = (int)Tools.parseDouble(label.Substring(0, 4), 0);
            else if (label.Length>=17 && label[5]=='-' && label[11]=='-')
                slice = (int)Tools.parseDouble(label.Substring(0, 5), 0);
            else if (label.Length>=20 && label[6]=='-' && label[13]=='-')
                slice = (int)Tools.parseDouble(label.Substring(0, 6), 0);
            return slice;
        }

        private void drawOverlay(Overlay overlay, Graphics g)
        {
            if (imp!=null && imp.getHideOverlay() && overlay!=showAllOverlay)
                return;
            flattening = imp!=null && ImagePlus.flattenTitle.Equals(imp.getTitle());
            if (imp!=null && showAllOverlay!=null && overlay!=showAllOverlay)
                overlay.drawLabels(false);
            Color labelColor = overlay.getLabelColor();
            if (labelColor==null) labelColor = Color.White;
            initGraphics(overlay, g, labelColor, Roi.getColor());
            int n = overlay.size();
            //if (IJ.debugMode) IJ.log("drawOverlay: "+n);
            int currentImage = imp!=null ? imp.getCurrentSlice() : -1;
            int stackSize = imp.getStackSize();
            if (stackSize==1)
                currentImage = -1;
            int channel = 0, slice = 0, frame = 0;
            bool hyperstack = imp.isHyperStack();
            if (hyperstack)
            {
                channel = imp.getChannel();
                slice = imp.getSlice();
                frame = imp.getFrame();
            }
            drawNames = overlay.getDrawNames() && overlay.getDrawLabels();
            bool drawLabels = drawNames || overlay.getDrawLabels();
            if (drawLabels)
                labelRects = new Rectangle[n];
            else
                labelRects = null;
            font = overlay.getLabelFont();
            if (overlay.scalableLabels() && font!=null)
            {
                double mag = getMagnification();
                if (mag!=1.0)
                    font = font.deriveFont((float)(font.Size*mag));
            }
            Roi activeRoi = imp.getRoi();
            bool roiManagerShowAllMode = overlay==showAllOverlay && !Prefs.showAllSliceOnly;
            for (int i = 0; i<n; i++)
            {
                if (overlay==null) break;
                Roi roi = overlay.get(i);
                if (roi==null) break;
                int c = roi.getCPosition();
                int z = roi.getZPosition();
                int t = roi.getTPosition();
                if (hyperstack)
                {
                    int position = roi.getPosition();
                    //IJ.log(c+" "+z+" "+t+"  "+position+" "+roiManagerShowAllMode);
                    if (position>0)
                    {
                        if (z==0 && imp.getNSlices()>1)
                            z = position;
                        else if (t==0)
                            t = position;
                    }
                    if (((c==0||c==channel) && (z==0||z==slice) && (t==0||t==frame)) || roiManagerShowAllMode)
                        drawRoi(g, roi, drawLabels ? i+LIST_OFFSET : -1);
                }
                else
                {
                    int position = stackSize>1 ? roi.getPosition() : 0;
                    if (position==0 && c==1)
                    {
                        if (z==1)
                            position = t;
                        else if (t==1)
                            position = z;
                    }
                    if (position==0 && stackSize>1)
                        position = getSliceNumber(roi.getName());
                    if (position>0 && imp.getCompositeMode()==IJ.COMPOSITE)
                        position = 0;
                    //IJ.log(position+"  "+currentImage+" "+roiManagerShowAllMode+" "+c+" "+z+" "+t);
                    if (position==0 || position==currentImage || roiManagerShowAllMode)
                        drawRoi(g, roi, drawLabels ? i+LIST_OFFSET : -1);
                }
            }
            //todo:
            // ((Graphics2D)g).setStroke(Roi.onePixelWide);
            drawNames = false;
            font = null;
        }

        void drawOverlay(Graphics g)
        {
            drawOverlay(imp.getOverlay(), g);
        }

        private void initGraphics(Overlay overlay, Graphics g, Color textColor, Color defaultColor)
        {
            if (smallFont==null)
            {
                smallFont = new Font("SansSerif", 9, FontStyle.Regular);
                largeFont = IJ.font12;
            }
            if (textColor!=Color.Empty)
            {
                labelColor = textColor;
                if (overlay!=null && overlay.getDrawBackgrounds())
                {
                    double brightness = (labelColor.R+labelColor.G+labelColor.B)/3.0;
                    if (labelColor==Color.Green) brightness = 255;
                    bgColor = brightness<=85 ? Color.White : Color.Black;
                }
                else
                    bgColor = Color.Empty;
            }
            else
            {
                int red = defaultColor.R;
                int green = defaultColor.G;
                int blue = defaultColor.B;
                if ((red+green+blue)/3<128)
                    labelColor = Color.White;
                else
                    labelColor = Color.Black;
                bgColor = defaultColor;
            }
            this.defaultColor = defaultColor;
            Font font = overlay!=null ? overlay.getLabelFont() : null;
            // if (font!=null && font.Size>12)
            //     ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            // g.setColor(defaultColor);
        }

        void drawRoi(Graphics g, Roi roi, int index)
        {
            ImagePlus imp2 = roi.getImage();
            roi.setImage(imp);
            Color saveColor = roi.getStrokeColor();
            if (saveColor==null)
                roi.setStrokeColor(defaultColor);
            // if (roi.getStroke()==null)
            //     ((Graphics2D)g).setStroke(Roi.onePixelWide);
            if (roi is TextRoi)
                ((TextRoi)roi).drawText(g);
            else
                roi.drawOverlay(g);
            roi.setStrokeColor(saveColor);
            if (index>=0)
            {
                // if (roi==currentRoi)
                //     g.setColor(Roi.getColor());
                // else
                //     g.setColor(defaultColor);
                drawRoiLabel(g, index, roi);
            }
            if (imp2!=null)
                roi.setImage(imp2);
            else
                roi.setImage(null);
        }

        void drawRoiLabel(Graphics g, int index, Roi roi)
        {
            if (roi.isCursor())
                return;
            bool pointRoi = roi is PointRoi;
            Rectangle r = roi.getBounds();
            int x = screenX(r.X);
            int y = screenY(r.Y);
            double mag = getMagnification();
            int width = (int)(r.Width*mag);
            int height = (int)(r.Height*mag);
            int size = width>40 || height>40 ? 12 : 9;
            int pointSize = 0;
            int crossSize = 0;
            if (pointRoi)
            {
                pointSize = ((PointRoi)roi).getSize();
                switch (pointSize)
                {
                    case 0: case 1: size=9; break;
                    case 2: case 3: size=10; break;
                    case 4: size=12; break;
                }
                crossSize = pointSize + 10 + 2*pointSize;
            }
            if (font!=null)
            {
                // g.setFont(font);
                size = (int)font.Size;
            }
            // else if (size==12)
            //     g.setFont(largeFont);
            // else
            //     g.setFont(smallFont);
            bool drawingList = index >= LIST_OFFSET;
            if (drawingList) index -= LIST_OFFSET;
            String label = "" + (index+1);
            if (drawNames)
                label = roi.getName();
            if (label==null)
                return;
            // FontMetrics metrics = g.getFontMetrics();
            // int w = metrics.stringWidth(label);
            // x = x + width/2 - w/2;
            // y = y + height/2 + Math.max(size/2, 6);
            // int h = metrics.getAscent() + metrics.getDescent();
            int xoffset = 0, yoffset = 0;
            // if (pointRoi)
            // {
            //     xoffset = 6 + pointSize;
            //     yoffset = h - 6 + pointSize;
            // }
            // if (bgColor!=null)
            // {
            //     int h2 = h;
            //     if (font!=null && font.Size>14)
            //         h2 = (int)(h2*0.8);
            //     // g.setColor(bgColor);
            //     // g.FillRoundRect(x-1+xoffset, y-h2+2+yoffset, w+1, h2-2, 5, 5);
            // }
            if (labelRects!=null && index<labelRects.Length)
            {
                if (pointRoi)
                {
                    int x2 = screenX(r.X);
                    int y2 = screenY(r.Y);
                    int crossSize2 = crossSize/2;
                    labelRects[index] = new Rectangle(x2-crossSize2, y2-crossSize2, crossSize, crossSize);
                }
                else
                {
                    // labelRects[index] = new Rectangle(x-3, y-h+1, w+4, h);
                }
            }
            //IJ.log("drawRoiLabel: "+" "+label+" "+x+" "+y+" "+flattening);
            // g.setColor(labelColor);
            // g.DrawString(label, x+xoffset, y-2+yoffset);
            // g.setColor(defaultColor);
        }



        /** Use ImagePlus.setOverlay(ij.gui.Overlay). */
        public void setOverlay(Overlay overlay)
        {
            imp.setOverlay(overlay);
        }


        void paintDoubleBuffered(Graphics g)
        {
            int srcRectWidthMag = (int)(srcRect.Width * magnification + 0.5);
            int srcRectHeightMag = (int)(srcRect.Height * magnification + 0.5);
            if (offScreenImage == null || offScreenWidth != srcRectWidthMag || offScreenHeight != srcRectHeightMag)
            {
                offScreenImage = new Bitmap(srcRectWidthMag, srcRectHeightMag);
                offScreenWidth = srcRectWidthMag;
                offScreenHeight = srcRectHeightMag;
            }
            Roi roi = imp.getRoi();
            try
            {
                if (imageUpdated)
                {
                    imageUpdated = false;
                    imp.updateImage();
                }

                Graphics offScreenGraphics = Graphics.FromImage(offScreenImage);
                setInterpolation(offScreenGraphics, Prefs.interpolateScaledImages);
                Image img = imp.getImage();
                if (img != null)
                {
                    //todo: GraphicsUnit.Pixel未必正确 
                    offScreenGraphics.DrawImage(img, new Rectangle(0, 0, srcRectWidthMag, srcRectHeightMag),
                    srcRect.X, srcRect.Y, srcRect.X + srcRect.Width, srcRect.Y + srcRect.Height, GraphicsUnit.Pixel);
                    // offScreenGraphics.drawImage(img, 0, 0, srcRectWidthMag, srcRectHeightMag,
                    //     srcRect.x, srcRect.y, srcRect.x + srcRect.width, srcRect.y + srcRect.height, null);
                }
                Overlay overlay = imp.getOverlay();
                if (overlay != null)
                    drawOverlay(overlay, offScreenGraphics);
                // if (showAllOverlay != null)
                //     drawOverlay(showAllOverlay, offScreenGraphics);
                if (roi != null)
                {
                    drawRoi(roi, offScreenGraphics);
                }
                if (srcRect.Width < imageWidth || srcRect.Height < imageHeight)
                {
                    // drawZoomIndicator(offScreenGraphics);
                }
                //if (IJ.debugMode) showFrameRate(offScreenGraphics);
                // g.drawImage(offScreenImage, 0, 0, null);
                this.Image = offScreenImage;
                this.Refresh();
            }
            catch (OutOfMemoryException e)
            {
                IJ.outOfMemory("Paint");
            }
        }

        /** Returns the current cursor location in image coordinates. */
        public Point getCursorLoc()
        {
            return new Point(xMouse, yMouse);
        }

        /** Returns 'true' if the cursor is over this image. */
        public bool cursorOverImage()
        {
            return !mouseExited;
        }

        /** Returns the mouse event modifiers. */
        public KeysMouseFlag getModifiers()
        {
            return flags;
        }

        /** Returns the ImagePlus object that is associated with this ImageCanvas. */
        public ImagePlus getImage()
        {
            return imp;
        }

        /** Sets the cursor based on the current tool and cursor location. */
        public void setCursor(int sx, int sy, int ox, int oy)
        {
            xMouse = ox;
            yMouse = oy;
            mouseExited = false;
            Roi roi = imp.getRoi();
            ImageWindow win = imp.getWindow();
            _overOverlayLabel = false;
            if (win == null)
                return;
            //todo:
            // if (IJ.spaceBarDown())
            // {
            //     setCursor(handCursor);
            //     return;
            // }
            int id = ToolBar.getToolId();
            switch (id)
            {
                case ToolBar.MAGNIFIER:
                    Cursor = Cursors.Arrow;
                    // setCursor(moveCursor);
                    break;
                case ToolBar.HAND:
                    Cursor = Cursors.Hand;
                    break;
                default:  //selection tool
                    PlugInTool tool = ToolBar.getPlugInTool();
                    bool arrowTool = roi != null && (roi is Arrow) && tool != null && "Arrow Tool".Equals(tool.getToolName());
                    if ((id >= ToolBar.CUSTOM1) && !arrowTool)
                    {
                        if (Prefs.usePointerCursor)
                            Cursor = Cursors.Default;
                        else
                            Cursor = Cursors.Cross;
                        // setCursor(crosshairCursor);
                    }
                    else if (roi != null && roi.getState() != Roi.CONSTRUCTING && roi.isHandle(sx, sy) >= 0)
                    {
                        Cursor = Cursors.Hand;
                    }
                    else if ((imp.getOverlay() != null || showAllOverlay != null) && overOverlayLabel(sx, sy, ox, oy) && (roi == null || roi.getState() != Roi.CONSTRUCTING))
                    {
                        _overOverlayLabel = true;
                        Cursor = Cursors.Hand;
                    }
                    else if (Prefs.usePointerCursor || (roi != null && roi.getState() != Roi.CONSTRUCTING && roi.contains(ox, oy)))
                        Cursor = Cursors.Default;
                    else
                        Cursor = Cursors.Cross;

                    break;
            }
        }

        private bool overOverlayLabel(int sx, int sy, int ox, int oy)
        {
            Overlay o = showAllOverlay;
            if (o == null)
                o = imp.getOverlay();
            if (o == null || !o.isSelectable() || !o.isDraggable() || !o.getDrawLabels() || labelRects == null)
                return false;
            for (int i = o.size() - 1; i >= 0; i--)
            {
                if (labelRects != null && labelRects[i] != null && labelRects[i].Contains(sx, sy))
                {
                    Roi roi = imp.getRoi();
                    if (roi == null || !roi.contains(ox, oy))
                        return true;
                    else
                        return false;
                }
            }
            return false;
        }

        /**Converts an offscreen x-coordinate to a screen x-coordinate.*/
        public int screenX(int ox)
        {
            return (int)((ox - srcRect.X) * magnification);
        }

        /**Converts an offscreen y-coordinate to a screen y-coordinate.*/
        public int screenY(int oy)
        {
            return (int)((oy - srcRect.Y) * magnification);
        }

        /**Converts a screen x-coordinate to an offscreen x-coordinate (nearest pixel center).*/
        public int offScreenX(int sx)
        {
            return srcRect.X + (int)(sx / magnification);
        }

        /**Converts a screen y-coordinate to an offscreen y-coordinate (nearest pixel center).*/
        public int offScreenY(int sy)
        {
            return srcRect.Y + (int)(sy / magnification);
        }

        /**Converts a screen x-coordinate to an offscreen x-coordinate (Roi coordinate of nearest pixel border).*/
        public int offScreenX2(int sx)
        {
            return srcRect.X + (int)Math.Round(sx / magnification);
        }

        /**Converts a screen y-coordinate to an offscreen y-coordinate (Roi coordinate of nearest pixel border).*/
        public int offScreenY2(int sy)
        {
            return srcRect.Y + (int)Math.Round(sy / magnification);
        }

        /**Converts a screen x-coordinate to a floating-point offscreen x-coordinate.*/
        public double offScreenXD(int sx)
        {
            return srcRect.X + sx / magnification;
        }

        /**Converts a screen y-coordinate to a floating-point offscreen y-coordinate.*/
        public double offScreenYD(int sy)
        {
            return srcRect.Y + sy / magnification;

        }

        /** Use ImagePlus.getOverlay(). */
        public Overlay getOverlay()
        {
            return imp.getOverlay();
        }

        public void repaintOverlay()
        {
            labelRects = null;
            Draw();
        }

        /**Converts a floating-point offscreen x-coordinate to a screen x-coordinate.*/
        public int screenXD(double ox)
        {
            return (int)((ox - srcRect.X) * magnification);
        }

        /**Converts a floating-point offscreen x-coordinate to a screen x-coordinate.*/
        public int screenYD(double oy)
        {
            return (int)((oy - srcRect.Y) * magnification);
        }

        public double getMagnification()
        {
            return magnification;
        }

        public void setMagnification(double magnification)
        {
            setMagnification2(magnification);
        }

        void setMagnification2(double magnification)
        {
            if (magnification > 32.0)
                magnification = 32.0;
            if (magnification < zoomLevels[0])
                magnification = zoomLevels[0];
            this.magnification = magnification;
            imp.setTitle(imp.getTitle());
        }

        /** Resizes the canvas when the user resizes the window. */
        void resizeCanvas(int width, int height)
        {
            throw new NotImplementedException();
            // ImageWindow win = imp.getWindow();
            // //IJ.log("resizeCanvas: "+srcRect+" "+imageWidth+"  "+imageHeight+" "+width+"  "+height+" "+dstWidth+"  "+dstHeight+" "+win.maxBounds);
            // if (!maxBoundsReset && (width > dstWidth || height > dstHeight) && win != null && win.maxBounds != null && width != win.maxBounds.width - 10)
            // {
            //     if (resetMaxBoundsCount != 0)
            //         resetMaxBounds(); // Works around problem that prevented window from being larger than maximized size
            //     resetMaxBoundsCount++;
            // }
            // if (scaleToFit || IJ.altKeyDown())
            // { fitToWindow(); return; }
            // if (width > imageWidth * magnification)
            //     width = (int)(imageWidth * magnification);
            // if (height > imageHeight * magnification)
            //     height = (int)(imageHeight * magnification);
            // Dimension size = getSize();
            // if (srcRect.width < imageWidth || srcRect.height < imageHeight || (painted && (width != size.width || height != size.height)))
            // {
            //     setSize(width, height);
            //     srcRect.width = (int)(dstWidth / magnification);
            //     srcRect.height = (int)(dstHeight / magnification);
            //     if ((srcRect.x + srcRect.width) > imageWidth)
            //         srcRect.x = imageWidth - srcRect.width;
            //     if ((srcRect.y + srcRect.height) > imageHeight)
            //         srcRect.y = imageHeight - srcRect.height;
            //     repaint();
            // }
            //IJ.log("resizeCanvas2: "+srcRect+" "+dstWidth+"  "+dstHeight+" "+width+"  "+height);
        }

        public void fitToWindow()
        {
            throw new NotImplementedException();
            // ImageWindow win = imp.getWindow();
            // if (win == null) return;
            // Rectangle bounds = win.getBounds();
            // Insets insets = win.getInsets();
            // int sliderHeight = win.getSliderHeight();
            // double xmag = (double)(bounds.width - (insets.left + insets.right + ImageWindow.HGAP * 2)) / srcRect.width;
            // double ymag = (double)(bounds.height - (ImageWindow.VGAP * 2 + insets.top + insets.bottom + sliderHeight)) / srcRect.height;
            // setMagnification(Math.min(xmag, ymag));
            // int width = (int)(imageWidth * magnification);
            // int height = (int)(imageHeight * magnification);
            // if (width == dstWidth && height == dstHeight) return;
            // srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
            // setSize(width, height);
            // getParent().doLayout();
        }

        void setMaxBounds()
        {
            if (maxBoundsReset)
            {
                maxBoundsReset = false;
                ImageWindow win = imp.getWindow();
                if (win != null && !IJ.isLinux() && win.maxBounds != null)
                {
                    win.setMaximizedBounds(win.maxBounds);
                    win.setMaxBoundsTime = DateTime.Now.Ticks;
                }
            }
        }

        void resetMaxBounds()
        {
            ImageWindow win = imp.getWindow();
            if (win != null && (DateTime.Now.Ticks - win.setMaxBoundsTime) > 500L)
            {
                win.setMaximizedBounds(win.maxWindowBounds);
                maxBoundsReset = true;
            }
        }

        private static double[] zoomLevels = new double[] {
            1/72.0, 1/48.0, 1/32.0, 1/24.0, 1/16.0, 1/12.0,
            1/8.0, 1/6.0, 1/4.0, 1/3.0, 1/2.0, 0.75, 1.0, 1.5,
            2.0, 3.0, 4.0, 6.0, 8.0, 12.0, 16.0, 24.0, 32.0 };

        public static double getLowerZoomLevel(double currentMag)
        {
            double newMag = zoomLevels[0];
            for (int i = 0; i < zoomLevels.Length; i++)
            {
                if (zoomLevels[i] < currentMag)
                    newMag = zoomLevels[i];
                else
                    break;
            }
            return newMag;
        }

        public static double getHigherZoomLevel(double currentMag)
        {
            double newMag = 32.0;
            for (int i = zoomLevels.Length - 1; i >= 0; i--)
            {
                if (zoomLevels[i] > currentMag)
                    newMag = zoomLevels[i];
                else
                    break;
            }
            return newMag;
        }



        private void mouseMoved(object? sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                mouseDragged(e);
                return;
            }

			//if (ij==null) return;
            int sx = e.X;
            int sy = e.Y;
            int ox = offScreenX(sx);
            int oy = offScreenY(sy);
            flags = new KeysMouseFlag(Control.ModifierKeys, e.Button);
            setCursor(sx, sy, ox, oy);
            mousePressedX = mousePressedY = -1;
            IJ.setInputEvent();
            PlugInTool tool = ToolBar.getPlugInTool();
            if (tool != null)
            {
                // tool.mouseMoved(imp, e);
                // if (e.isConsumed()) return;
            }
            Roi roi = imp.getRoi();
            int type = roi != null ? roi.getType() : -1;
            if (type > 0 && (type == Roi.POLYGON || type == Roi.POLYLINE || type == Roi.ANGLE || type == Roi.LINE)
                         && roi.getState() == Roi.CONSTRUCTING)
            {
                roi.mouseMoved(e);
            }
            else
            {
                if (ox < imageWidth && oy < imageHeight)
                {
                    ImageWindow win = imp.getWindow();
                    // Cursor must move at least 12 pixels before text
                    // displayed using IJ.showStatus() is overwritten.
                    if ((sx - sx2) * (sx - sx2) + (sy - sy2) * (sy - sy2) > 144)
                        showCursorStatus = true;
                    if (win != null && showCursorStatus)
                    {
                        win.mouseMoved(ox, oy);
                    }
                        
                }
                else
                    IJ.ShowStatus("");
            }
		}

        private void mouseEntered(object? sender, EventArgs e)
        {
            PlugInTool tool = ToolBar.getPlugInTool();
            if (tool != null)
                tool.mouseEntered(imp, e);
        }


        private void mousePressed(object? sender, MouseEventArgs e)
        {
			showCursorStatus = true;
			int toolID = ToolBar.getToolId();
			ImageWindow win = imp.getWindow();
			if (win != null && win.running2 && toolID != ToolBar.MAGNIFIER)
			{
				if (win is StackWindow)
				((StackWindow)win).setAnimate(false);
			else
					win.running2 = false;
				return;
			}

			int x = e.X;
			int y = e.Y;
            flags = new KeysMouseFlag(Control.ModifierKeys, e.Button);
			// if (toolID != ToolBar.MAGNIFIER && (e.isPopupTrigger() || (!IJ.isMacintosh() && (flags & Event.META_MASK) != 0)))
			// {
			// 	handlePopupMenu(e);
			// 	return;
			// }

			int ox = offScreenX(x);
			int oy = offScreenY(y);
			xMouse = ox; yMouse = oy;
			// if (IJ.spaceBarDown())
			// {
			// 	// temporarily switch to "hand" tool of space bar down
			// 	setupScroll(ox, oy);
			// 	return;
			// }

			// if (overOverlayLabel && (imp.getOverlay() != null || showAllOverlay != null))
			// {
			// 	if (activateOverlayRoi(ox, oy))
			// 		return;
			// }

			if ((DateTime.Now.Ticks - mousePressedTime) < 300L && !drawingTool())
			{
				if (activateOverlayRoi(ox, oy))
					return;
			}

			mousePressedX = ox;
			mousePressedY = oy;
			mousePressedTime = DateTime.Now.Ticks;

			PlugInTool tool = ToolBar.getPlugInTool();
			if (tool != null)
			{
				// tool.mousePressed(imp, e);
				// if (e.isConsumed()) return;
			}
			if (customRoi && imp.getOverlay() != null)
				return;

			if (toolID >= ToolBar.CUSTOM1)
			{
				// if (tool != null && "Arrow Tool".equals(tool.getToolName()))
				// 	handleRoiMouseDown(e);
				// else
				// 	Toolbar.getInstance().runMacroTool(toolID);
				// return;
			}

			// final Roi roi1 = imp.getRoi();
			// final int size1 = roi1 != null ? roi1.size() : 0;
			// final Rectangle r1 = roi1 != null ? roi1.getBounds() : null;

			switch (toolID)
			{
				case ToolBar.MAGNIFIER:
			// 		if (IJ.shiftKeyDown())
			// 			zoomToSelection(ox, oy);
			// 		else if ((flags & (Event.ALT_MASK | Event.META_MASK | Event.CTRL_MASK)) != 0)
			// 		{
			// 			zoomOut(x, y);
			// 			if (getMagnification() < 1.0)
			// 				imp.repaintWindow();
			// 		}
			// 		else
			// 		{
			// 			zoomIn(x, y);
			// 			if (getMagnification() <= 1.0)
			// 				imp.repaintWindow();
			// 		}
					break;
			// 	case Toolbar.HAND:
			// 		setupScroll(ox, oy);
			// 		break;
			// 	case Toolbar.DROPPER:
			// 		setDrawingColor(ox, oy, IJ.altKeyDown());
			// 		break;
			// 	case Toolbar.WAND:
			// 		double tolerance = WandToolOptions.getTolerance();
			// 		Roi roi = imp.getRoi();
			// 		if (roi != null && (tolerance == 0.0 || imp.isThreshold()) && roi.contains(ox, oy))
			// 		{
			// 			Rectangle r = roi.getBounds();
			// 			if (r.width == imageWidth && r.height == imageHeight)
			// 				imp.deleteRoi();
			// 			else if (!e.isAltDown())
			// 			{
			// 				handleRoiMouseDown(e);
			// 				return;
			// 			}
			// 		}
			// 		if (roi != null)
			// 		{
			// 			int handle = roi.isHandle(x, y);
			// 			if (handle >= 0)
			// 			{
			// 				roi.mouseDownInHandle(handle, x, y);
			// 				return;
			// 			}
			// 		}
			// 		if (!imp.okToDeleteRoi())
			// 			break;
			// 		setRoiModState(e, roi, -1);
			// 		String mode = WandToolOptions.getMode();
			// 		if (Prefs.smoothWand)
			// 			mode = mode + " smooth";
			// 		int npoints = IJ.doWand(imp, ox, oy, tolerance, mode);
			// 		if (Recorder.record && npoints > 0)
			// 		{
			// 			if (Recorder.scriptMode())
			// 				Recorder.recordCall("IJ.doWand(imp, " + ox + ", " + oy + ", " + tolerance + ", \"" + mode + "\");");
			// 			else
			// 			{
			// 				if (tolerance == 0.0 && mode.equals("Legacy"))
			// 					Recorder.record("doWand", ox, oy);
			// 				else
			// 					Recorder.recordString("doWand(" + ox + ", " + oy + ", " + tolerance + ", \"" + mode + "\");\n");
			// 			}
			// 		}
			// 		break;
				case ToolBar.OVAL:
					if (ToolBar.getBrushSize() > 0)
						new RoiBrush();
					else
						handleRoiMouseDown(e);
					break;
				default:  //selection tool
					handleRoiMouseDown(e);
                    break;
			}

			// if (longClickDelay > 0)
			// {
			// 	if (pressTimer == null)
			// 		pressTimer = new java.util.Timer();
			// 	final Point cursorLoc = getCursorLoc();
			// 	pressTimer.schedule(new TimerTask()
			// 	{
			// 	public void run()
			// 	{
			// 		if (pressTimer != null)
			// 		{
			// 			pressTimer.cancel();
			// 			pressTimer = null;
			// 		}
			// 		Roi roi2 = imp.getRoi();
			// 		int size2 = roi2 != null ? roi2.size() : 0;
			// 		Rectangle r2 = roi2 != null ? roi2.getBounds() : null;
			// 		boolean empty = r2 != null && r2.width == 0 && r2.height == 0;
			// 		int state = roi2 != null ? roi2.getState() : -1;
			// 		boolean unchanged = state != Roi.MOVING_HANDLE && r1 != null && r2 != null && r2.x == r1.x
			// 			&& r2.y == r1.y && r2.width == r1.width && r2.height == r1.height && size2 == size1
			// 			&& !(size2 > 1 && state == Roi.CONSTRUCTING);
			// 		boolean cursorMoved = !getCursorLoc().equals(cursorLoc);
			// 		//IJ.log(size2+" "+empty+" "+unchanged+" "+state+" "+roi1+"  "+roi2);			
			// 		if ((roi1 == null && (size2 <= 1 || empty)) || unchanged)
			// 		{
			// 			if (roi1 == null) imp.deleteRoi();
			// 			if (!cursorMoved && Toolbar.getToolId() != Toolbar.HAND)
			// 				handlePopupMenu(e);
			// 		}
			// 	}
			// }, longClickDelay);
		}

        private bool drawingTool()
        {
            int id = ToolBar.getToolId();
            return id == ToolBar.POLYLINE || id == ToolBar.FREELINE || id >= ToolBar.CUSTOM1;
        }

        /**
        * @deprecated
        * replaced by ImagePlus.getOverlay()
        */
        public ArrayList getDisplayList()
        {
            Overlay overlay = imp.getOverlay();
            if (overlay == null)
                return null;
            ArrayList displayList = new ArrayList();
            for (int i = 0; i < overlay.size(); i++)
                displayList.Add(overlay.get(i));
            return displayList;
        }

        public void mouseReleased(object? sender, MouseEventArgs e)
        {
            // if (pressTimer != null)
            // {
            //     pressTimer.cancel();
            //     pressTimer = null;
            // }

            int ox = offScreenX(e.X);
            int oy = offScreenY(e.Y);
            Overlay overlay = imp.getOverlay();
            //todo:
            if ((overlay != null || showAllOverlay != null) && ox == mousePressedX && oy == mousePressedY)
            {
                // bool cmdDown = IJ.isMacOSX() && e.isMetaDown();
                // Roi roi = imp.getRoi();
                // if (roi != null && roi.getBounds().Width == 0)
                //     roi = null;
                // if ((e.isAltDown() || e.isControlDown() || cmdDown) && roi == null)
                // {
                //     if (activateOverlayRoi(ox, oy))
                //         return;
                // }
                // else if ((System.currentTimeMillis() - mousePressedTime) > 250L && !drawingTool())
                // { // long press
                //     if (activateOverlayRoi(ox, oy))
                //         return;
                // }
            
            }
            
            PlugInTool tool = ToolBar.getPlugInTool();
            if (tool != null)
            {
                // tool.mouseReleased(imp, e);
                // if (e.isConsumed()) return;
            }
            // flags = e.getModifiers();
            // flags &= ~InputEvent.BUTTON1_MASK; // make sure button 1 bit is not set
            // flags &= ~InputEvent.BUTTON2_MASK; // make sure button 2 bit is not set
            // flags &= ~InputEvent.BUTTON3_MASK; // make sure button 3 bit is not set
            Roi roi = imp.getRoi();
            if (roi != null)
            {
                Rectangle r = roi.getBounds();
                int type = roi.getType();
                if ((r.Width == 0 || r.Height == 0)
                    && !(type == Roi.POLYGON || type == Roi.POLYLINE || type == Roi.ANGLE || type == Roi.LINE)
                    && !(roi is TextRoi)
                    && roi.getState() == Roi.CONSTRUCTING
                    && type != Roi.POINT)
                    imp.deleteRoi();
                else
                    roi.handleMouseUp(e.X, e.Y);
            }
        }

        private bool activateOverlayRoi(int ox, int oy)
        {
            int currentImage = -1;
            int stackSize = imp.getStackSize();
            if (stackSize > 1)
                currentImage = imp.getCurrentSlice();
            int channel = 0, slice = 0, frame = 0;
            bool hyperstack = imp.isHyperStack();
            if (hyperstack)
            {
                channel = imp.getChannel();
                slice = imp.getSlice();
                frame = imp.getFrame();
            }
            Overlay o = showAllOverlay;
            if (o == null)
                o = imp.getOverlay();
            if (o == null || !o.isSelectable())
                return false;
            bool roiManagerShowAllMode = o == showAllOverlay && !Prefs.showAllSliceOnly;
            bool labels = o.getDrawLabels();
            int sx = screenX(ox);
            int sy = screenY(oy);
            for (int i = o.size() - 1; i >= 0; i--)
            {
                Roi roi = o.get(i);
                if (roi == null)
                    continue;
                //IJ.log(".isAltDown: "+roi.contains(ox, oy));
                bool containsMousePoint = false;
                if (roi is Line) {  //grab line roi near its center
                    //todo:
                    // double grabLineWidth = 1.1 + 5.0/ magnification;
                    // containsMousePoint = (((Line)roi).getFloatPolygon(grabLineWidth)).contains(ox, oy);
                }
                else
                {
                    containsMousePoint = roi.contains(ox, oy);
                }
                if (containsMousePoint || (labels && labelRects != null && labelRects[i] != null && labelRects[i].Contains(sx, sy)))
                {
                    if (hyperstack && roi.getPosition() == 0)
                    {
                        int c = roi.getCPosition();
                        int z = roi.getZPosition();
                        int t = roi.getTPosition();
                        if (!((c == 0 || c == channel) && (z == 0 || z == slice) && (t == 0 || t == frame) || roiManagerShowAllMode))
                            continue;
                    }
                    else
                    {
                        int position = stackSize > 1 ? roi.getPosition() : 0;
                        if (!(position == 0 || position == currentImage || roiManagerShowAllMode))
                            continue;
                    }
                    if (!IJ.altKeyDown() && roi.getType() == Roi.COMPOSITE
                    && roi.getBounds().Width == imp.getWidth() && roi.getBounds().Height == imp.getHeight())
                        return false;
                    //if (Toolbar.getToolId()==Toolbar.OVAL && Toolbar.getBrushSize()>0)
                    //	Toolbar.getInstance().setTool(Toolbar.RECTANGLE);
                    roi.setImage(null);
                    imp.setRoi(roi);
                    roi.handleMouseDown(sx, sy);
                    roiManagerSelect(roi, false);
                    ResultsTable.selectRow(roi);
                    return true;
                }
            }
		    return false;
	    }

        public bool roiManagerSelect(Roi roi, bool delete)
        {
            //todo:
            // RoiManager rm = RoiManager.getInstance();
            // if (rm == null)
            //     return false;
            // int index = rm.getRoiIndex(roi);
            // if (index < 0)
            //     return false;
            // if (delete)
            // {
            //     rm.select(imp, index);
            //     rm.runCommand("delete");
            // }
            // else
            //     rm.selectAndMakeVisible(imp, index);
            return true;
        }

        public void mouseDragged(MouseEventArgs e)
        {
            int x = e.X;
            int y = e.Y;
            xMouse = offScreenX(x);
            yMouse = offScreenY(y);
            flags = new KeysMouseFlag(Control.ModifierKeys, e.Button); 
            mousePressedX = mousePressedY = -1;
            //IJ.log("mouseDragged: "+flags);
            // if (flags == 0)  // workaround for Mac OS 9 bug
            //     flags = InputEvent.BUTTON1_MASK;
            // if (ToolBar.getToolId() == ToolBar.HAND || IJ.spaceBarDown())
            //     scroll(x, y);
            // else
            {
                PlugInTool tool = ToolBar.getPlugInTool();
                if (tool != null)
                {
                    // tool.mouseDragged(imp, e);
                    // if (e.isConsumed()) return;
                }
                IJ.setInputEvent();
                Roi roi = imp.getRoi();
                if (roi != null)
                    roi.handleMouseDrag(x, y, flags);
            }
        }


        protected void handleRoiMouseDown(MouseEventArgs e)
		{
			int sx = e.X;
			int sy = e.Y;
			int ox = offScreenX(sx);
			int oy = offScreenY(sy);
			Roi roi = imp.getRoi();
			int tool = ToolBar.getToolId();

			int handle = roi != null ? roi.isHandle(sx, sy) : -1;
			bool multiPointMode = roi != null && (roi is PointRoi) && handle == -1
				  && tool == ToolBar.POINT && ToolBar.getMultiPointMode();
			if (multiPointMode)
			{
				double oxd = roi.offScreenXD(sx);
				double oyd = roi.offScreenYD(sy);
				//todo:
				// if (e.isShiftDown() && !IJ.isMacro())
				// {
				// 	FloatPolygon points = roi.getFloatPolygon();
				// 	if (points.npoints > 0)
				// 	{
				// 		double x0 = points.xpoints[0];
				// 		double y0 = points.ypoints[0];
				// 		double slope = Math.abs((oxd - x0) / (oyd - y0));
				// 		if (slope >= 1.0)
				// 			oyd = points.ypoints[0];
				// 		else
				// 			oxd = points.xpoints[0];
				// 	}
				// }
				// ((PointRoi)roi).addUserPoint(imp, oxd, oyd);
				// imp.setRoi(roi);
				return;
			}

			if (roi != null && (roi is PointRoi)) {
				// int npoints = ((PolygonRoi)roi).getNCoordinates();
				// if (npoints > 1 && handle == -1 && !IJ.altKeyDown() && !(tool == Toolbar.POINT && !Toolbar.getMultiPointMode() && IJ.shiftKeyDown()))
				// {
				// 	String msg = "Type shift-a (Edit>Selection>Select None) to delete\npoints. Use multi-point tool to add points.";
				// 	GenericDialog gd = new GenericDialog("Point Selection");
				// 	gd.addMessage(msg);
				// 	gd.addHelp(PointToolOptions.help);
				// 	gd.hideCancelButton();
				// 	gd.showDialog();
				// 	return;
				// }
			}

			setRoiModState(e, roi, handle);
			if (roi != null)
			{
				//todo:
				// if (handle >= 0)
				// {
				// 	roi.mouseDownInHandle(handle, sx, sy);
				// 	return;
				// }
				// Rectangle r = roi.getBounds();
				// int type = roi.getType();
				// if (type == Roi.RECTANGLE && r.Width == imp.getWidth() && r.Height == imp.getHeight()
				// && roi.getPasteMode() == Roi.NOT_PASTING && !(roi is ImageRoi)) {
				// 	imp.deleteRoi();
				// 	return;
				// }
				// if (roi.contains(ox, oy))
				// {
				// 	if (roi.modState == Roi.NO_MODS)
				// 		roi.handleMouseDown(sx, sy);
				// 	else
				// 	{
				// 		imp.deleteRoi();
				// 		imp.createNewRoi(sx, sy);
				// 	}
				// 	return;
				// }
				// bool segmentedTool = tool == ToolBar.POLYGON || tool == ToolBar.POLYLINE || tool == ToolBar.ANGLE;
				// if (segmentedTool && (type == Roi.POLYGON || type == Roi.POLYLINE || type == Roi.ANGLE)
				// && roi.getState() == roi.CONSTRUCTING)
				// 	return;
				// if (segmentedTool && !(IJ.shiftKeyDown() || IJ.altKeyDown()))
				// {
				// 	imp.deleteRoi();
				// 	return;
				// }
			}
			imp.createNewRoi(sx, sy);
		}

        void setRoiModState(MouseEventArgs e, Roi roi, int handle)
        {
            if (roi == null || (handle >= 0 && roi.modState == Roi.NO_MODS))
                 return;
             if (roi.state == Roi.CONSTRUCTING)
                 return;
             int tool = ToolBar.getToolId();
             if (tool > ToolBar.FREEROI && tool != ToolBar.WAND && tool != ToolBar.POINT)
             {
                 roi.modState = Roi.NO_MODS; return;
             }
             if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift)
                 roi.modState = Roi.ADD_TO_ROI;
             else if ((Control.ModifierKeys & Keys.Alt) == Keys.Alt)
                 roi.modState = Roi.SUBTRACT_FROM_ROI;
             else
                 roi.modState = Roi.NO_MODS;
            IJ.log("setRoiModState: "+roi.modState+" "+ roi.state);
        }

        public void setShowAllList(Overlay showAllList)
        {
            this.showAllOverlay = showAllList;
            labelRects = null;
        }

        public Overlay getShowAllList()
        {
            return showAllOverlay;
        }

        [Obsolete]
        public void setShowAllROIs(bool showAllROIs)
        {
            RoiManager rm = RoiManager.getInstance();
            if (rm!=null)
                rm.runCommand(showAllROIs ? "show all" : "show none");
        }

        [Obsolete]
        public bool getShowAllROIs()
        {
            return getShowAllList()!=null;
        }


        public void setScaleToFit(bool scaleToFit)
        {
            this.scaleToFit = scaleToFit;
        }

        public bool getScaleToFit()
        {
            return scaleToFit;
        }
    }
}
